package com.mall.common.service.impl;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.Transient;
import javax.persistence.criteria.CriteriaQuery;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.mall.common.bean.Order;
import com.mall.common.bean.Pager;
import com.mall.common.bean.SearchFilter;
import com.mall.common.dao.BaseDao;
import com.mall.common.hibernate.Finder;
import com.mall.common.hibernate.Updater;
import com.mall.common.hibernate.Updater.UpdateMode;
import com.mall.common.service.BaseService;

/**
 * Service实现类 - Service实现类基类
 */
@Service
@Transactional(readOnly=true)
public class BaseServiceImpl<T, PK extends Number> implements BaseService<T, PK> {

	protected Log log = LogFactory.getLog(getClass());
	
	private BaseDao<T, PK> baseDao;
	
	private static final String[] allIgnoreProperties = { "id", "createDate", "modifyDate" };

	public BaseDao<T, PK> getBaseDao() {
		return baseDao;
	}

	public void setBaseDao(BaseDao<T, PK> baseDao) {
		this.baseDao = baseDao;
	}
	
	public EntityManager getEntityManager(){
		return baseDao.getEntityManager();
	}

	public T find(PK id) {
		return baseDao.find(id);
	}
	
	public List<T> get(PK[] ids) {
		return baseDao.get(ids);
	}
	
	@Override
	public List<T> get(String[] ids) {
		return baseDao.get(ids);
	}
	
	public T get(String propertyName, Object value) {
		return baseDao.get(propertyName, value);
	}
	
	public List<T> getList(String propertyName, Object value) {
		return baseDao.getList(propertyName, value);
	}
	
	public List<T> getListLike(String propertyName, String value, Integer module){
		return baseDao.getListLike(propertyName, value, module);
	}
	
	public List<T> getListByIds(Long[] ids){
		return baseDao.getList(ids);
	}

	public List<T> getAll() {
		return baseDao.getAll();
	}
	
	public Long getTotalCount() {
		return baseDao.getTotalCount();
	}

	public boolean isUnique(String propertyName, Object oldValue, Object newValue) {
		return baseDao.isUnique(propertyName, oldValue, newValue);
	}

	@Override
	public boolean isUniqueById(String propertyName, Long id, Object newValue) {
		Finder finder = Finder.create("from ").append(baseDao.getEntityClass().getSimpleName())
				.append(" bean where bean.id!=:id and bean.").append(propertyName).append("=:newValue");
		finder.setParam("id", id);
		finder.setParam("newValue", newValue);
		Object obj = finder.findUniqueByHQL();
		return obj == null;
	}
	
	public boolean isExist(String propertyName, Object value) {
		return baseDao.isExist(propertyName, value);
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public void persist(T entity) {
		baseDao.persist(entity);
	}

	@Override
	public void refresh(T entity) {
		Assert.notNull(entity);
		baseDao.refresh(entity);
	}
	
	@Override
	public Pager findPage(Pager pager, List<SearchFilter> filterList, List<Order> orderList){
		if(pager == null){
			pager = new Pager();
		}
		pager.setFilterList(filterList);
		pager.setOrderList(orderList);
		return baseDao.findPage(pager);
	}
	
	@Override
	public Pager findPage(Pager pager, List<SearchFilter> filterList){
		if(pager == null){
			pager = new Pager();
		}
		pager.setFilterList(filterList);
		return baseDao.findPage(pager);
	}
	
	@Override
	public Pager findPage(Pager pager){
		return baseDao.findPage(pager);
	}

	@Override
	public Pager findByPager(Pager pager, Finder finder) {
		return baseDao.findByPager(pager, finder);
	}
	
	@Override
	public Pager findByPager(CriteriaQuery<T> criteriaQuery, Pager pager){
		return baseDao.findByPager(criteriaQuery, pager);
	}
	
	@Override
	public List<T> findList(Integer first, Integer count, List<SearchFilter> filters, List<Order> orders){
		return baseDao.findList(first, count, filters, orders);
	}
	
	@Override
	public List<T> findList(CriteriaQuery<T> criteriaQuery, Integer first, Integer count, 
			List<SearchFilter> filterList, List<Order> orderList){
		return baseDao.findList(criteriaQuery, first, count, filterList, orderList);
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public void remove(T entity) {
		baseDao.remove(entity);
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public void remove(PK id) {
		Assert.notNull(id, "ids must not be empty");
		baseDao.remove(this.find(id));
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public void remove(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		if(ids != null){
			for (PK id : ids) {
				T entity = find(id);
				remove(entity);
			}
		}
	}
	
	
	@Transactional(propagation=Propagation.REQUIRED)
	public T update(T entity, String ignoreProperties[]) {
		Assert.notNull(entity);
		if (baseDao.isManaged(entity)){
			throw new IllegalArgumentException("Entity must not be managed");
		}
		T persistentObj = baseDao.find(baseDao.getIdentifier(entity));
		if (persistentObj != null) {
			update(entity, persistentObj, (String[])ArrayUtils.addAll(ignoreProperties, allIgnoreProperties));
			return baseDao.merge(persistentObj);
		} else {
			return baseDao.merge(entity);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void update(Object Source, Object Target, String as[]) {
		Assert.notNull(Source, "Source must not be null");
		Assert.notNull(Target, "Target must not be null");
		PropertyDescriptor[] apropertydescriptor = BeanUtils.getPropertyDescriptors(Target.getClass());
		List<String> list = as == null ? null : Arrays.asList(as);
		for (int i = 0; i < apropertydescriptor.length; i++) {
			PropertyDescriptor propertydescriptor = apropertydescriptor[i];
			if (propertydescriptor.getWriteMethod() != null && (as == null || !list.contains(propertydescriptor.getName()))) {
				PropertyDescriptor propertydescriptor1 = BeanUtils.getPropertyDescriptor(Source.getClass(), propertydescriptor.getName());
				if (propertydescriptor1 != null && propertydescriptor1.getReadMethod() != null){
					try {
						//获取可读写的方法get方法
						Method getMethod = propertydescriptor1.getReadMethod();
						//判断get方法是否是公有属性，如果不是，则设置为可访问
						if (!Modifier.isPublic(getMethod.getDeclaringClass().getModifiers())){
							getMethod.setAccessible(true);
						}
						Object sourcePropValue = getMethod.invoke(Source, new Object[0]);
						Object targetPropValue = getMethod.invoke(Target, new Object[0]);
						if (sourcePropValue != null && targetPropValue != null && (targetPropValue instanceof Collection)) {
							Collection collection = (Collection)targetPropValue;
							collection.clear();
							collection.addAll((Collection)sourcePropValue);
						} else {
							Method writeMethod = propertydescriptor.getWriteMethod();
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())){
								writeMethod.setAccessible(true);
							}
							writeMethod.invoke(Target, sourcePropValue);
						}
					} catch (Throwable throwable) {
						throw new FatalBeanException("Could not copy properties from source to target", throwable);
					}
				}
			}
		}

	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public T update(T entity){
		Assert.notNull(entity);
		return baseDao.merge(entity);
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public T updateByUpdater(T entity){
		if (baseDao.isManaged(entity)){
			throw new IllegalArgumentException("Entity must not be managed");
		}
		Updater<T> updater = new Updater<T>(entity);
		entity = this.updateByUpdater(updater);
		return entity;
	}
	
	@SuppressWarnings("unchecked")
	@Transactional(propagation=Propagation.REQUIRED)
	public T updateByUpdater(Updater<T> updater) {
		T entity = updater.getBean();
		if (baseDao.isManaged(entity)){
			throw new IllegalArgumentException("Entity must not be managed");
		}
		T persistentObj = this.find((PK)baseDao.getIdentifier(entity));
		if(persistentObj != null){
			PropertyDescriptor[] apropertydescriptor = BeanUtils.getPropertyDescriptors(entity.getClass());
			updaterCopyToPersistentObject(updater, persistentObj, apropertydescriptor);
			return update(persistentObj);
		} else {
			return update(entity);
		}
	}
	
	/**
	 * 将更新对象拷贝至实体对象，并处理many-to-one的更新。
	 * 
	 * @param updater
	 * @param persistentObj
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void updaterCopyToPersistentObject(Updater<T> updater, T persistentObj, PropertyDescriptor[] apropertydescriptor) {
		List<String> allIgnorePropertiesList = Arrays.asList(allIgnoreProperties);
		T target = updater.getBean();
		for (int i = 0; i < apropertydescriptor.length; i++) {
			PropertyDescriptor propertydescriptor = apropertydescriptor[i];
			String propName = propertydescriptor.getName();
			if (allIgnorePropertiesList.contains(propName)) {
				continue;
			}
			
			try {
				//获取可读写的方法get方法
				Method getMethod = propertydescriptor.getReadMethod();
				if(getMethod.isAnnotationPresent(Transient.class) || propName.equals("class")){
					continue;
				}
				//判断get方法是否是公有属性，如果不是，则设置为可访问
				if (!Modifier.isPublic(getMethod.getDeclaringClass().getModifiers())){
					getMethod.setAccessible(true);
				}
				
				Object targetPropValue = getMethod.invoke(target, new Object[0]);
				if (!updater.isUpdate(propName, targetPropValue)) {
					continue;
				}
				Object sourcePropValue = getMethod.invoke(persistentObj, new Object[0]);
				if (targetPropValue != null && sourcePropValue != null && (sourcePropValue instanceof Collection)) {
					Collection collection = (Collection)sourcePropValue;
					collection.clear();
					collection.addAll((Collection)targetPropValue);
				}else{		
					Method writeMethod = propertydescriptor.getWriteMethod();
					writeMethod.invoke(persistentObj, targetPropValue);
				}
			} catch (Exception e) {
				throw new RuntimeException(
						"copy property to persistent object failed: '"
								+ propName + "'", e);
			}
		}
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public T updateByUpdateMode(T entity, UpdateMode mode) {
		Updater<T> updater = new Updater<T>(entity, mode);
		entity = this.updateByUpdater(updater);
		return entity;
	}

//	public WebApplicationException createWebApplicationException(String errorMsg) {
//		ResponseBuilder builder = Response.serverError();
//		Response response = builder.build();
//		return new WebApplicationException(new Throwable(errorMsg),response);
//	}
	
	@Override
	public Map<String, Object> integrationParameter(Map<String, Object> filterParams) {
		//会员查询
		String memberDatas = (String) filterParams.get("EQ_memberDatas");
		if(!StringUtils.isBlank(memberDatas)){
			if(Pattern.compile("^((13)|(15)|(18))\\d{9}$").matcher(memberDatas).matches()){
				filterParams.put("EQ_mobile", memberDatas);
			}else if(Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$").matcher(memberDatas).matches()){
				filterParams.put("EQ_email", memberDatas);
			}else{
				filterParams.put("LIKE_username", memberDatas);
			}
			filterParams.remove("EQ_memberDatas");
		}
		//关联关系的会员查询
		String relevanceMemberDatas = (String) filterParams.get("EQ_relevanceMemberDatas");
		if(!StringUtils.isBlank(relevanceMemberDatas)){
			if(Pattern.compile("^((13)|(15)|(18))\\d{9}$").matcher(relevanceMemberDatas).matches()){
				filterParams.put("EQ_member.mobile", relevanceMemberDatas);
			}else if(Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$").matcher(relevanceMemberDatas).matches()){
				filterParams.put("EQ_member.email", relevanceMemberDatas);
			}else{
				filterParams.put("LIKE_member.username", relevanceMemberDatas);
			}
			filterParams.remove("EQ_relevanceMemberDatas");
		}
		return filterParams;
	}

	@Override
	public Pager findByPagerForHessian(Pager pager, Finder finder) {
		return baseDao.findByPagerForHessian(pager, finder);
	}

}
